;
;	    inf.mac  Ver 1.3   rev  23/Jan/1983
;	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;
;	 cp/m 2.+ disk information display program
;	 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;
;This program displays all the information that CP/M has hidden
;away in the bios and other places. This is very useful if
;testing a new drive or CP/M implementation (which is why i
;wrote it. It displays all the information that the CP/M bdos
;uses and thus you can determine that 'DPH' for that drive is
;functioning correctly.
;
;Command structure.....
;
;inf	for the logged drive or....
;inf d: to look at the info for drive 'd'
;
;
;	Copyright (c) 1982,83 by Steven Engel
;
;
;Version list, most recent first
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;
;version 1.3, fixed the printout of the translate table so it
;	      uses the bios call 'sectran' to find the physical
;	      sectors. This should make it more universal.
;
;version 1.2 & earlier, initial releases.
;
;
;if your cp/m system is non-standard in any of the start
;address offsets from the bios or if your ccp is
;a non-standard length change the ccplen, ccpo, bdoso equates.
;
bdos	equ	5		;bdos call location
bios	equ	1		;bios warm boot jump location
ccplen	equ	0800h		;STANDARD ccp length
ccpo	equ	01600h		;STANDARD ccp offset from bios
bdoso	equ	0e00h		;STANDARD bdos offset from bios
tpa	equ	0100h		;tpa (standard one)
cr	equ	0dh
lf	equ	0ah
base	equ	0
defstf	equ	base+4		;user level+default drive
fcb	equ	base+05ch	;file control block
line	equ	30		;number of chars on one line
				;for the allocation map display

	aseg			;absolutely
	.z80			;the only way to go
	org	tpa

	jp	start

signon: defm	cr,lf,'CP/M 2.2 disk informer'
	defm	' version 1.3 for drive '
drvnum: defm	'A:, user level '
usrlev: defm	'00',cr,lf
	defm	'CCP is at  $'
bdos@:	defm	cr,lf,'BDOS is at $'
bios@:	defm	cr,lf,'BIOS is at $'
space:	defm	cr,lf,'TPA space with ccp intact is $'
space2: defm	cr,lf,'Space with ccp overlayed is $'
sorry:	defm	cr,lf,lf,'Sorry this program requires'
	defm	' CP/M 2.0 or later to run',cr,lf,'$'
ntabl:	defm	cr,lf,'This drive uses 1:1 physical to'
	defm	' sector translation',cr,lf,'$'
ttabl:	defm	cr,lf,'Translate table for this drive is:'
	defm	cr,lf,'$'
csv@:	defm	cr,lf,'Software check vector (csv) is at $'
alv@:	defm	cr,lf,'Allocation storage scratchpad (alv)'
	defm	' is at $'
dchar:	defm	' sectors per track and a max. of $'
dent:	defm	' directory entries'
pnta:	defm	cr,lf,'Directory block allocation:-',cr,lf
	defm	9,'   AL0     AL1   ',cr,lf,9,'$'
dirblk: defm	' dir block(s) on a $'
tdisk:	defm	'k disk with $'
kused:	defm	'k used and $'
sfree:	defm	'k free$'

nres:	defm	' system tracks '
bksiz:	defm	'with $'
kpblk:	defm	'k per block and skew factor of $'

baddpb: defm	cr,lf,lf,'This dpb is stuffed, check your'
	defm	' bios',cr,lf,lf,'$'

dmap:	defm	cr,lf,'Disk storage allocation map:-'
	defm	cr,lf,9,9,'$'
tblk:	defm	cr,lf,9,'Total blocks: $'
used:	defm	'  blocks used: $'
left:	defm	'  blocks free: $'

	defs	40		;our stack
ccpsp:	defs	2		;ccp stack pointer
block:	defs	2		;# of blocks used
free:	defs	2		;# of blocks free
bsize:	defs	1		;our k/block
ddisk:	defs	1		;default disk

;dph & dpb storage area

xltadr: defs	2		;disk trans table address
	defs	2		;bdos scratch area
	defs	2		; "      "     "
	defs	2		; "      "     "
	defs	2		;address of dir buffer
dpb:	defs	2		;dpb for logged drive
csv:	defs	2		;csv for this drive
alv:	defs	2		;alv for this drive

spt:	defs	2		;sectors per track
bsh:	defs	1		;block shift factor
blm:	defs	1
exm:	defs	1		;extent mask
dsm:	defs	2		;total # of blocks
drm:	defs	2		;total # of dir entries
al0:	defs	1		;alloc0
al1:	defs	1		;alloc1
cks:	defs	2		;size of the dir check vector
off:	defs	2		;# of reserved tracks

bls:	defb	10,19,36,69,134 ;bsh+blm

start:	ld	hl,0
	add	hl,sp
	ld	(ccpsp),hl
	ld	sp,ccpsp	;make our stack here
	ld	hl,0		;init
	ld	(block),hl	;init block
	ld	(free),hl	; and block free
	call	verify		;ensure cpm 2.0 or later
	ld	hl,(bios)	;install address for seldsk
	ld	l,01bh		;l=lsb of bios jmp table seldsk
	ld	(disk+1),hl	;save in our jump
	ld	l,030h		;l=lsb of jmp table sectran
	ld	(sectran+1),hl	;save in our space

;get the drive # and xfer some info
;note: the user level display stuff assumes a 0-15 user level
;ccp, so if you are running a non-standard ccp it won't work.

	ld	a,(fcb)		;get currently logged disk
	or	a		;0 ?
	jr	nz,gotdrv
	ld	a,(defstf)	;get default drive
	push	af		;save it
	and	0f0h		;mask off drive stuff
	rrca			;get user level
	rrca			;into first 4 bits
	rrca
	rrca
	cp	10		;is it > 9 ?
	jr	c,notgrt
	push	af		;save the number
	ld	a,'1'		;Ok, save the 1 then
	ld	(usrlev),a
	pop	af		;restore original
	sub	10		;make it now 0-5
notgrt: add	a,'0'		;make it ascii 0-15
	ld	(usrlev+1),a	;save for the message
	pop	af		;restore user+drive stuff
	and	0fh		;mask off user stuff
	jr	drv

gotdrv: dec	a		;make it 0-15
drv:	ld	c,a		;put it in c
	add	a,'A'		;make it ascii 'A'-'P'
	ld	(drvnum),a	;save it for the message
	push	bc		;save it
	call	seldsk		;for the bdos
	pop	bc
	call	disk		;go and select it, hl will=dph
				;address filled in above
	ld	de,xltadr	;address pointer
	ld	bc,16		;number to move
	ldir			;move 'em

	ld	de,signon
	call	msgout
	ld	hl,(bios)	;get bios location
	ld	l,0		;make it cold boot address
	push	hl		;save for later
	ld	de,-ccpo	;ccp offset
	add	hl,de		;hl now points to the ccp
	pop	de		;get bios pointer in de
	push	hl		;save ccp pointer
	push	de		;save bios pointer
	call	hexout		;print hex out of hl
	ld	de,bdos@	;print bdos message
	call	msgout
	pop	hl		;get bios pointer back
	push	hl		;save it again
	ld	de,-bdoso	;bdos offset
	add	hl,de
	call	hexout		;print it in hex
	ld	de,bios@	;print bios message
	call	msgout
	pop	hl		;get bios pointer back
	call	hexout
	ld	de,space	;print tpa space
	call	msgout
	pop	hl		;get ccp pointer
	ld	de,-tpa		;tpa
	add	hl,de
	push	hl		;save this address pointer
	call	decout		;print it in decimal
	ld	de,space2	;print space w/out tpa
	call	msgout
	pop	hl
	ld	de,ccplen	;STANDARD ccp length
	add	hl,de		;add it on to last count
	call	decout		;and print it decimally
	call	crlf

;Now determine some information about the disk system

	ld	de,csv@		;tell user where csv is
	call	msgout
	ld	hl,(csv)
	call	hexout
	ld	de,alv@		;and where the alv is too
	call	msgout
	ld	hl,(alv)
	call	hexout

	ld	a,(xltadr)	;get lsb of xlt address
	or	a		;is it 0
	jr	nz,okxlt
	ld	a,(xltadr+1)	;get msb
	or	a
	jr	nz,okxlt
	ld	de,ntabl	;tell user no sector trans
	call	msgout
	jr	tdpb

okxlt:	call	crlf
	ld	hl,(dpb)	;first we have to get # of secs
	ld	de,spt		;save in our internal one
	ld	bc,15
	ldir			;move 'em

	ld	de,ttabl	;trans table message
	call	msgout
	ld	hl,(spt)	;get # of sects/track
	ld	bc,0		;the sector counter

pntsec: push	bc
	push	de		;save these
	push	hl
	ld	de,(xltadr)	;point to the trans table
	call	sectran		;get the first sector
	call	decout		;hl has #, print it decimally
	ld	a,','		;a delimiter
	call	chrout
	pop	hl
	pop	de		;restore these
	pop	bc
	inc	bc		;inc for next sector
	dec	hl
	ld	a,h		;get sector count
	or	l		;check for 0
	jr	nz,pntsec	;loop until so

tdpb:	ld	hl,(alv)	;point to allocation info
	ld	de,(dsm)	;get total block count-1
	inc	de		;make it total block count
	call	pntmap		;go and print a disk map

	ld	hl,(spt)	;get sectors count
	call	decout		;print it
	ld	de,dchar	;point to message
	call	msgout
	ld	hl,(drm)	;total # of dir entries
	inc	hl
	call	decout
	ld	de,dent
	call	msgout
	ld	a,(al0)		;get al0
	call	binout		;print it in binary
	ld	a,' '		;and a space
	call	chrout
	ld	a,(al1)		;get al1
	call	binout		;print it in binary
	call	crlf
	call	crlf
	ld	hl,(off)	;get reserved track count
	call	decout
	ld	de,nres
	call	msgout

	ld	a,(bsh)		;get bsh
	ld	b,a
	ld	a,(blm)
	add	a,b		;get this value
	ld	hl,bls		;point to bls table (blm+bsh)
	ld	c,a		;put bsh+blm here for compare
	ld	d,1		;k block count
	ld	b,5		;# to check
check:	ld	a,(hl)		;get first one
	cp	c		;OK ?
	jr	z,blok		;yes-it's ok
	ld	a,d
	add	a,a		;*2
	ld	d,a
	inc	hl
	djnz	check		;loop for all
	ld	de,baddpb
	call	msgout
	jp	exit

blok:	ld	a,d
	ld	(bsize),a	;save block k size
	ld	h,0
	ld	l,a
	call	decout		;print it decimally

	ld	de,kpblk	;print k/block
	call	msgout

	ld	a,(bsh)		;get the block shift factor
	ld	l,a
	ld	h,0
	call	decout
	call	crlf

	call	fndblk		;check allocated dir blocks
	push	de
	ex	de,hl
	call	decout
	ld	de,dirblk	;directory blocks
	call	msgout

	pop	de		;get dir blocks back
	ld	hl,(dsm)	;get total # of blocks-1
	inc	hl		;make it proper
	or	a
	sbc	hl,de		;sub # of allocated dir blocks
	ld	a,(bsize)	;get # of k/block
	dec	a		;make it -1
	ld	b,a
	push	bc		;save this
	push	hl		;save block count
	call	add		;add 'em up
	ld	de,tdisk	;disk size message
	call	msgout

	pop	hl		;restore total block count
	ld	de,(free)	;get free blocks
	or	a
	sbc	hl,de		;get # blocks used
	pop	bc
	push	bc
	push	de		;total block count
	call	add		;add 'em up and print it
	ld	de,kused
	call	msgout

	pop	hl		;get free # of blocks (pntmap)
	pop	bc		;get block size back
	call	add		;add 'em up
	ld	de,sfree
	call	msgout

exit:	ld	a,(ddisk)	;get disk drive back
	ld	c,a
	push	bc
	call	disk		;install it as before
	pop	bc
	call	seldsk		;for the bdos
	ld	hl,(ccpsp)	;get ccp stack pointer back
	ld	sp,hl		;remake it
	ret			;and return

pntmap: push	de		;save bit count
	push	hl		;save byte pointer
	ld	de,dmap		;tell user what's happening
	call	msgout
	pop	hl
	pop	de
	ld	c,line		;# on one line
map:	ld	a,(hl)		;get a byte
	push	hl		;save this
	ld	b,8		;8 bits/byte
bpnt1:	rla			;rotate bit into carry
	push	af		;save this
	jr	c,one		;was it a one ?
	ld	hl,(free)	;get free block count
	inc	hl		;inc it
	ld	(free),hl	;save it again
	ld	a,'0'		;make a 0
	jr	pnt

one:	ld	hl,(block)	;get used block count
	inc	hl		;inc it
	ld	(block),hl	;save it again
	ld	a,'1'		;make a one
pnt:	call	chrout		;print it
	dec	c		;dec line count
	call	z,newl
	dec	de		;dec bit counter
	ld	a,d		;check for end of bits
	or	e
	jr	z,leave		;end ?? yes-finished
	pop	af		;restore char
	djnz	bpnt1		;loop for 8 bits of the byte
	pop	hl		;restore byte pointer
	inc	hl		;inc to next byte
	jr	map		;loop for next byte fetch

leave:	pop	hl		;relieve stack
	pop	af

	ld	de,tblk
	call	msgout
	ld	hl,(dsm)	;get total block count-1
	inc	hl		;make it correct
	call	decout

	ld	de,used
	call	msgout
	ld	hl,(block)	;get used block count
	call	decout

	ld	de,left
	call	msgout
	ld	hl,(free)	;get # of free blocks
	call	decout

crlf:	ld	a,0dh
	call	chrout
	ld	a,0ah
	call	chrout

chrout: push	hl
	push	bc
	push	de
	ld	e,a
	ld	c,2		;send char to console
	call	bdos
	pop	de
	pop	bc
	pop	hl
	ret

;check al0 & al1 for directory block allocation and return # of
;allocated blocks in de

fndblk: ld	de,0		;init counter
	ld	b,8		;bit counter
	ld	a,(al0)		;first this one
	call	incblk		;count some bits
	ld	b,8		;bit counter
	ld	a,(al1)		;and next this one

incblk: rla			;rotate thru cy, set if block
	jr	nc,inc		;no-don't inc bit counter
	inc	de		;inc block counter
inc:	djnz	incblk		;loop for all bits
	ret

;add up (*2) the number in HL, B times and print it decimally

add:	add	hl,hl
	djnz	add		;add 'em all up
	push	hl
	call	decout
	pop	hl
	ret

newl:	call	crlf
	ld	a,9
	call	chrout
	ld	a,9
	call	chrout
	ld	c,line
	ret

msgout: ld	c,9		;print $ terminated string
bcall:	jp	bdos

seldsk: ld	e,c		;select disk in 'C'
	ld	c,14
	jr	bcall

disk:	jp	0000		;jump to select disk
				;filled in at init time
sectran:jp	0000		;jump to sectran
				;filled in at init time

;print 16 bit value in HL in decimal

decout: push	hl
	call	bdc
	call	prtx
	pop	hl
	ret

bdc:	push	de
	push	bc
	ld	de,string
loop:	call	div10
	add	a,'0'
	dec	de
	ld	(de),a
	ld	a,h
	or	l
	jr	nz,loop
	ex	de,hl
	pop	bc
	pop	de
	ret

div10:	ld	c,16
	xor	a
loop1:	adc	hl,hl
	rla
	dec	c
	jr	z,term
	cp	10
	ccf
	jr	nc,loop1
	sub	10
	scf
	jr	loop1

term:	cp	10
	ccf
	jr	nc,bot
	sub	10
	scf
bot:	adc	hl,hl
	ret

prtx:	ld	a,(hl)
	cp	'$'
	ret	z
	push	hl
	call	chrout
	pop	hl
	inc	hl
	jr	prtx

	defs	5
string: defm	'$'

hexout: push	hl		;save hex value
	ld	a,'0'		;a 0 first
	call	chrout		;print it
	pop	hl		;restore it
	ld	a,h		;get msb
	call	hexout2		;print it
	ld	a,l		;get lsb
	call	hexout2		;print it
	ld	a,'h'		;tack a 'h' on the end
	jp	chrout		;print it

hexout2:push	af
	and	0f0h		;mask right off
	rrca			;move
	rrca			;  it
	rrca			;   to
	rrca			;     the right
	call	hexout3		;print it
	pop	af		;get char back
	and	0fh		;mask left half off
hexout3:cp	10		;need letter
	jr	c,hexout4	;no
	add	a,7		;adjust for a-f
hexout4:add	a,'0'		;make it ascii
	push	hl
	call	chrout		;print it and return
	pop	hl
	ret

;output in binary the value in A

binout: ld	b,8		;number to move
bino1:	rla			;move it left
	push	af		;save rotated value
	jr	c,p1
	ld	a,'0'
	jr	pnt1

p1:	ld	a,'1'
pnt1:	call	chrout
	pop	af
	djnz	bino1
	ret

;verify that we are running on a CP/M 2.0 or later

verify: ld	c,12
	call	bdos
	cp	20h		;cp/m 2.0 or later
	ret	nc		;yes !!!!
	ld	de,sorry	;tell user
	call	msgout
	jp	exit		;and exit

	end
